package cn.amossun.starter.event.handler;

import cn.amossun.starter.event.property.EventContext;
import cn.amossun.starter.event.property.EventExchangeContext;
import cn.amossun.starter.event.property.EventQueueContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.InitializingBean;

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
public class EventBindingInitializer implements InitializingBean {

    private EventContext eventContext;

    private RabbitAdmin rabbitAdmin;

    public EventBindingInitializer(EventContext eventContext, RabbitAdmin rabbitAdmin) {
        this.eventContext = eventContext;
        this.rabbitAdmin = rabbitAdmin;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initialize();
    }

    void initialize() {
        Map<String, EventQueueContext> queueMap = eventContext.getQueueContexts().stream().collect(Collectors.toMap(EventQueueContext::getQueueName, Function.identity(), (exiting, replacement) -> exiting));

        Map<String, EventExchangeContext> exchangeMap = eventContext.getExchangeContexts().stream().collect(Collectors.toMap(EventExchangeContext::getExchangeName, Function.identity(), (exiting, replacement) -> exiting));

        eventContext.getBindingContexts().stream().forEach(bindingContext -> {
            Binding binding = null;
            EventExchangeContext eventExchangeContext = exchangeMap.get(bindingContext.getExchangeName());

            EventQueueContext eventQueueContext = queueMap.get(bindingContext.getQueueName());

            BindingBuilder.DestinationConfigurer destinationConfigurer = eventQueueContext != null && eventQueueContext.getQueue() != null ? BindingBuilder.bind(eventQueueContext.getQueue()) : BindingBuilder.bind(eventExchangeContext.getExchange());
            switch (bindingContext.getExchangeTypeEnum().getTypeValue()) {
                case ExchangeTypes.TOPIC:
                    binding = destinationConfigurer.to((TopicExchange)eventExchangeContext.getExchange()).with(bindingContext.getRoutingKey());
                    break;
                case ExchangeTypes.DIRECT:
                    binding = destinationConfigurer.to((DirectExchange)eventExchangeContext.getExchange()).with(bindingContext.getRoutingKey());
                    break;
                case ExchangeTypes.HEADERS:
                    binding = destinationConfigurer.to((HeadersExchange)eventExchangeContext.getExchange()).whereAll(bindingContext.getArgumentsMap()).match();
                    break;
                case ExchangeTypes.FANOUT:
                    binding = destinationConfigurer.to((FanoutExchange)eventExchangeContext.getExchange());
                    break;
                case ExchangeTypes.SYSTEM:
                    binding = destinationConfigurer.to(eventExchangeContext.getExchange()).with(bindingContext.getRoutingKey()).and(bindingContext.getArgumentsMap());
                    break;
            }
            if(binding == null) {
                log.warn("The current exchangeType '{}' is not initialized.", bindingContext.getExchangeTypeEnum().getTypeValue());
                return;
            }
            rabbitAdmin.declareBinding(binding);
        });
    }

}
